package com.qyb.base.java8.chap1;

import com.qyb.base.java8.bean.Apple;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

import static java.util.stream.Collectors.toList;

/**
 * *************************************************************************************************
 * <p/>
 * 实现功能：
 * <p>
 * ------------------------------------------------------------------------------------------------
 * 版本          变更时间             变更人                     变更原因
 * ------------------------------------------------------------------------------------------------
 * 1.0.00      2017/8/3 14:10      秦有宝                     新建
 * <p/>
 * *************************************************************************************************
 */
public class FilteringApples {

    public static void demo1(String ... args){

        List<Apple> inventory = Arrays.asList(new Apple(80,"green"),
                new Apple(155, "green"),
                new Apple(120, "red"));

        // [Apple{color='green', weight=80}, Apple{color='green', weight=155}]
        List<Apple> greenApples = filterApples(inventory, FilteringApples::isGreenApple);
        System.out.println(greenApples);

        // [Apple{color='green', weight=155}]
        List<Apple> heavyApples = filterApples(inventory, FilteringApples::isHeavyApple);
        System.out.println(heavyApples);

        //匿名函数或Lambda
        //但要是Lambda的长度多于几行（它的行为也不是一目了然）的话，
        //那你还是应该用方法引用来指向一个有描述性名称的方法，而不是使用匿名的Lambda。你应该
        //以代码的清晰度为准绳。
        // [Apple{color='green', weight=80}, Apple{color='green', weight=155}]
        List<Apple> greenApples2 = filterApples(inventory, (Apple a) -> "green".equals(a.getColor()));
        System.out.println(greenApples2);

        // [Apple{color='green', weight=155}]
        List<Apple> heavyApples2 = filterApples(inventory, (Apple a) -> a.getWeight() > 150);
        System.out.println(heavyApples2);

        // []
        List<Apple> weirdApples = filterApples(inventory, (Apple a) -> a.getWeight() < 80 || "brown".equals(a.getColor()));
        System.out.println(weirdApples);

        /*
        Collection主要是为了存储和访问数据，
        Stream则主要用于描述对数据的计算。
        筛选一个Collection（将filterApples应用在一个List上）的最快方法常常是将其转换为Stream，进行并行处理，然后再转换回List，
         */
        //顺序处理：
        List<Apple> heavyApples3 = inventory.stream().filter((Apple a) -> a.getWeight() > 150).collect(toList());
        System.out.println(heavyApples3);

        //并行处理：
        List<Apple> heavyApples4 = inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150).collect(toList());
    }

    /**
     * 在Java 8之前,选出所有的绿苹果
     * @param inventory
     * @return
     */
    public static List<Apple> filterGreenApples(List<Apple> inventory){
        List<Apple> result = new ArrayList<>();
        for (Apple apple: inventory){
            if ("green".equals(apple.getColor())) {
                result.add(apple);
            }
        }
        return result;
    }

    /**
     * 在Java 8之前,选出重的苹果，比如超过150克
     * @param inventory
     * @return
     */
    public static List<Apple> filterHeavyApples(List<Apple> inventory){
        List<Apple> result = new ArrayList<>();
        for (Apple apple: inventory){
            if (apple.getWeight() > 150) {
                result.add(apple);
            }
        }
        return result;
    }

    public static boolean isGreenApple(Apple apple) {
        return "green".equals(apple.getColor());
    }

    public static boolean isHeavyApple(Apple apple) {
        return apple.getWeight() > 150;
    }
    /*
        方法作为Predicate参数p传递进去

        什么是谓词？
        前面的代码传递了方法Apple::isGreenApple（它接受参数Apple并返回一个
        boolean）给filterApples，后者则希望接受一个Predicate<Apple>参数。谓词（predicate）
        在数学上常常用来代表一个类似函数的东西，它接受一个参数值，并返回true或false。你
        在后面会看到，Java 8也会允许你写Function<Apple,Boolean>——在学校学过函数却没学
        过谓词的读者对此可能更熟悉，但用Predicate<Apple>是更标准的方式，效率也会更高一
        点儿，这避免了把boolean封装在Boolean里面。
     */
    public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(p.test(apple)){      //苹果符合p所代表的条件吗
                result.add(apple);
            }
        }
        return result;
    }

}
